package org.smarthings.things.automation.rest.test;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.smarthings.things.automation.Rule;
import org.smarthings.things.automation.RuleCollection;
import org.smarthings.things.commons.Paginator;
import org.smarthings.things.commons.utils.PaginationUtils;

public class MockedRuleCollection implements RuleCollection {

	// in general this numerator shall load portions of rules form the database
	// main idea is not to keep all rules in memory

	private List<Rule> rules;

	public MockedRuleCollection(List<Rule> rules) {
		this.rules = rules;
	}

	public Iterator<Rule> iterator() {
		return rules.iterator();
	}

	public Paginator<Rule> paginator(int offset, int limit) {
		validateOffset(offset);
		validateLimit(limit);

		return new RulePaginator(offset, limit);
	}

	private void validateOffset(int offset) throws IllegalArgumentException {
		if (rules.isEmpty()) {
			if (offset != 0) {
				throw new IllegalArgumentException("Invalid offset " + offset);
			}
		} else if (offset < 0 || offset > rules.size() - 1) {
			throw new IllegalArgumentException("Invalid offset " + offset);
		}
	}

	private void validateLimit(int limit) throws IllegalArgumentException {
		if (limit <= 0) {
			throw new IllegalArgumentException("Invalid limit " + limit);
		}
	}

	private class RulePaginator implements Paginator<Rule> {

		private int offset;
		private int limit;

		public RulePaginator(int offset, int limit) {
			this.offset = offset;
			this.limit = limit;
		}

		public List<Rule> next() {
			if (rules.isEmpty()) {
				return Collections.emptyList();
			}
			if (offset == rules.size()) {
				return null; // end of pages
			}
			int size = rules.size();
			int fromIndex = offset;
			int toIndex = PaginationUtils.calculateNextPageOffset(offset, limit, size);
			offset = toIndex;
			return Collections.unmodifiableList(rules.subList(fromIndex, toIndex));
		}

		public List<Rule> previous() {
			if (rules.isEmpty()) {
				return Collections.emptyList();
			}
			if (offset == 0) {
				return null; // end of pages
			}
			int size = rules.size();
			int fromIndex = PaginationUtils.calculatePreviousPageOffset(offset, limit, size);
			int toIndex = offset;
			offset = fromIndex;
			return Collections.unmodifiableList(rules.subList(fromIndex, toIndex));
		}

		public List<Rule> first() {
			if (rules.isEmpty()) {
				return Collections.emptyList();
			}
			int size = rules.size();
			int fromIndex = 0;
			int toIndex = PaginationUtils.calculateFirstPageSize(offset, limit, size);
			return Collections.unmodifiableList(rules.subList(fromIndex, toIndex));
		}

		public List<Rule> last() {
			if (rules.isEmpty()) {
				return Collections.emptyList();
			}
			int size = rules.size();
			int toIndex = size;
			int fromIndex = PaginationUtils.calculateLastPageSize(offset, limit, size);
			return Collections.unmodifiableList(rules.subList(fromIndex, toIndex));
		}

	}

}
